
package stratego.java.ssm.online;

import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;

import stratego.java.ssm.OnlineGame;

/***
 * This class servers as a conversation connection for messages and moves between the players
 * 
 * @author Sabina Smajlaj
 * 
 */
public class Messages implements Runnable
{
	
	public AtomicBoolean	inGame, inSetup;
	private Thread	     keepAlive;
	private int	         timeout;
	private Scanner	     netIn;
	private PrintWriter	 netOut;
	private OnlineGame	 game;
	private String[]	 lastMove;
	
	public Messages(final JTextArea input, Scanner is, PrintWriter os, OnlineGame game, int timeout)
	        throws IOException
	{
		String TEXT_SUBMIT = "text-submit";
		String INSERT_BREAK = "insert-break";
		InputMap inputMap = input.getInputMap();
		KeyStroke enter = KeyStroke.getKeyStroke("ENTER");
		KeyStroke shiftEnter = KeyStroke.getKeyStroke("shift ENTER");
		inputMap.put(shiftEnter, INSERT_BREAK); // input.get(enter)) = "insert-break"
		inputMap.put(enter, TEXT_SUBMIT);
		
		ActionMap actions = input.getActionMap();
		actions.put(TEXT_SUBMIT, new AbstractAction()
		{
			private static final long	serialVersionUID	= 8596859423423987442L;
			
			@Override
			public void actionPerformed(ActionEvent e)
			{
				String message = input.getText();
				input.setText("");
				while (message.endsWith("\n"))
					message = message.substring(0, message.lastIndexOf("\n"));
				if (!message.isEmpty())
				{
					System.out.println("Me: " + message);
					netOut.println("0" + message);
				}
			}
		});
		// Setting the fields of this object for use in the run method
		this.netIn = is;
		this.netOut = os;
		this.game = game;
		inGame = new AtomicBoolean(false);
		inSetup = new AtomicBoolean(false);
	}
	
	// A thread to continuously receive messages as well as to send keep alive messages. The thread
	// encapsulated by this object does not actually send out piece information; rather, it is up to
	// OnlineGame do send those messages.
	@Override
	public void run()
	{
		// Start a keep alive thread
		// If timeout is 0, then timeout has been disabled (see the note on
		// conn.getSoTimeout(). And this thread is not needed
		if (timeout != 0)
		{
			keepAlive = new Thread(new Runnable()
			{
				@Override
				public void run()
				{
					netOut.println("2");
					try
					{
						// Send a keep alive message every half the timeout time
						Thread.sleep(timeout / 2);
					}
					catch (InterruptedException e)
					{
						e.printStackTrace();
					}
				}
			});
			keepAlive.start();
		}
		while (netIn.hasNext())
		{
			recieveMove(netIn.nextLine());
		}
	}
	
	/***
	 * Send move to opponent (passed to online game)
	 * 
	 * @param String
	 *            string from online game with move information
	 */
	public synchronized void sendMessage(String gameMessage)
	{
		synchronized (netOut)
		{
			netOut.println(gameMessage);
		}
	}// end method
	
	/***
	 * Receive movement or board placement from opponent (passed from online game) get info from
	 * scanner and call method on online game move: 1x1.y1.x2.y2 length 4 piece placement length 3
	 */
	private void recieveMove(String line)
	{
		if (line.startsWith("0"))
		{
			while (line.endsWith("\n"))
				line = line.substring(0, line.lastIndexOf("\n"));
			System.out.println("Them: " + line.substring(1));
		}
		else if (line.startsWith("1"))
		{ // checking to see if it's a move
			line = line.substring(1);
			String[] array = line.split("\\.");
			int a = array.length;
			
			if (a == 4)
			{
				// It got a piece movement message even though it's not in game mode.
				if (!inGame.get())
					return;
				int y = Integer.parseInt(array[1]);
				array[1] = Integer.toString(game.board.getBoard().length - 1 - y);
				y = Integer.parseInt(array[3]);
				array[3] = Integer.toString(game.board.getBoard().length - 1 - y);
				if (lastMove == null)
					lastMove = array;
				else
				{
					if (Arrays.equals(lastMove, array))
						return;
					lastMove = array;
				}
				System.out.println("Last Opponent Move: " + Arrays.toString(array));
				game.setOpponentMove(array);
			}
			else if (a == 3)
			{// if it's not a move, it's a board placement
				// It got a piece placement method even though it's not in setup mode.
				if (!inSetup.get())
					return;
				int y = Integer.parseInt(array[1]);
				array[1] = Integer.toString(game.board.getBoard().length - 1 - y);
				game.setOpponentPiece(array); // send placement to online game
			}
			// We are being notified of the turn we are taking
			else if (!inGame.get() && !inSetup.get())
			{
				if (line.equals("0") || line.equals("1"))
					game.setFirstPlayer(line);
			}
		}// end else if
		
	}// end
	
	/**
	 * <code>public void close()</code>
	 * <p>
	 * Closes the Scanner and PrintWriter that are sending and receiving messages, and also stops
	 * the keepAlive thread.
	 * </p>
	 */
	public void close() throws InterruptedException
	{
		if (keepAlive != null)
			keepAlive.interrupt();
		netOut.flush();
		netOut.close();
		netIn.close();
	}
}// end class
